package main

import (
	"fmt"
	"log"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/go-xorm/xorm"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"

	"encoding/json"
	"strconv"
)

var x *xorm.Engine

type Menu struct{
	Id int `xorm:"pk autoincr"`
	Name string
	ParentMenuId int
	ThisRoleMenu bool `xorm:"-"`
	SubmenuList []*Menu `xorm:"-"`
	CreatedAt time.Time
  	UpdatedAt time.Time
}

type Role struct {
	Id int `xorm:"pk autoincr"`
	Name string
	MenuList []*Menu `xorm:"-"`
	CreatedAt time.Time
  	UpdatedAt time.Time
}

type RoleMenus struct{
	Id int `xorm:"pk autoincr"`
	RoleId int
	MenuId int
}

func init() {
	var xErr error
	x, xErr = xorm.NewEngine("mysql", "root:new_password@tcp(127.0.0.1:3306)/recursion?charset=utf8")
	if xErr != nil {
		log.Fatal("数据库连接失败:", xErr)
	}
	if err := x.Sync(new(Menu), new(Role), new(RoleMenus)); err != nil {
		log.Fatal("数据表同步失败:", err)
	}
}

type MenuController struct{
    Ctx  iris.Context
}

func GetMenuList(pm *Menu){
	
	x.Where("parent_menu_id = ?", pm.Id).Find(&pm.SubmenuList)
	if pm.SubmenuList == nil {
		return 
	}else{
		for _, v := range pm.SubmenuList{
			GetMenuList(v)
		}
	}
}

func GetRoleMenuList(pm *Menu, roleid string){
	
	x.Where("parent_menu_id = ?", pm.Id).Find(&pm.SubmenuList)
	if pm.SubmenuList == nil {
		return 
	}else{
		for _, v := range pm.SubmenuList{
			var rm RoleMenus
			x.Where("menu_id = ?", v.Id).And("role_id = ?", roleid).Get(&rm)
			if rm.Id != 0{
				v.ThisRoleMenu = true
			}else{
				v.ThisRoleMenu = false
			}
			GetRoleMenuList(v, roleid)
		}
	}
}

func PutRoleMenuList(mu *Menu, roleid string){
 
	if mu.SubmenuList != nil {
		for _, v := range mu.SubmenuList{

			var rm RoleMenus
			x.Where("menu_id = ?", v.Id).And("role_id = ?", roleid).Get(&rm)
			if v.ThisRoleMenu == true{
				if rm.Id == 0 {
					rm.MenuId = v.Id
					rm.RoleId, _= strconv.Atoi(roleid)
					x.Insert(&rm)
				}
			}else{
				if rm.Id != 0 {
					x.ID(rm.Id).Delete(&rm)
				}
			}

			PutRoleMenuList(v, roleid)
		}
	}
}

func GetMyMenuList(mu *Menu, roleid string){

	//x.Join("INNER", "role_menus", "role_menus.menu_id = menu.id").
	//	Where("menu.parent_menu_id = ?", mu.Id).
	//	And("role_menus.role_id = ?", roleid).
	//	Find(&mu.SubmenuList)

	x.Where("parent_menu_id = ?", mu.Id).Find(&mu.SubmenuList)
	if mu.SubmenuList == nil {
		return 
	}else{
		j := 0
		for _, v := range mu.SubmenuList {
			var rm RoleMenus
			x.Where("menu_id = ?", v.Id).And("role_id = ?", roleid).Get(&rm)
			if rm.Id != 0 {
				v.ThisRoleMenu = true
				mu.SubmenuList[j] = v
				j++
			}

			GetMyMenuList(v, roleid)
		}

		mu.SubmenuList = mu.SubmenuList[:j]
	}
}

func (c *MenuController) PostQuerymenu() mvc.Result { 

	menuid := c.Ctx.FormValue("menuid")

	var pm Menu
	x.Where("id = ?", menuid).Get(&pm)

	// var m Menu
	// x.Where("id = ?", 34).Get(&m)
	// fmt.Println(m)

	var ml []*Menu
	err := x.Where("created_at > ?", "2020-05-07 00:00:00").Find(&ml)
	if err != nil{
		fmt.Println("error: ",err)
	}
	fmt.Println("time: ",ml)

	fmt.Println(&pm)
	if pm.Id == 0{
		return mvc.Response{
			Object : map[string]interface{}{"message": "找不到菜单", "data":nil, "code":400,},
		}
	}
	
	GetMenuList(&pm)

	return mvc.Response{
        Object : map[string]interface{}{"message": "成功", "data":pm, "code":200, "timeQuery":ml},
    }
}

func (c *MenuController) PostQueryrolemenu() mvc.Result { 

	menuid := c.Ctx.FormValue("menuid")
	roleid := c.Ctx.FormValue("roleid")

	fmt.Println(menuid)
	fmt.Println(roleid)

	var pm Menu
	x.Where("id = ?", menuid).Get(&pm)

	var rm RoleMenus
	_, err := x.Where("menu_id = ?", menuid).And("role_id = ?", roleid).Get(&rm)
	if err != nil{
		fmt.Println(err)
	}
	fmt.Println(rm)
	if rm.Id != 0{
		pm.ThisRoleMenu = true
	}else{
		pm.ThisRoleMenu = false
	}

	fmt.Println(&pm)
	if pm.Id == 0{
		return mvc.Response{
			Object : map[string]interface{}{"message": "找不到菜单", "data":nil, "code":400,},
		}
	}
	
	GetRoleMenuList(&pm, roleid)

	return mvc.Response{
        Object : map[string]interface{}{"message": "成功", "data":pm, "code":200,},
    }
}

func (c *MenuController) PostInsertrolemenu() mvc.Result { 

	roleid := c.Ctx.FormValue("roleid")
	rolemenustr := c.Ctx.FormValue("rolemenustr")

	fmt.Println(roleid)

	var mu Menu
	if err := json.Unmarshal([]byte(rolemenustr), &mu); err == nil{
		PutRoleMenuList(&mu, roleid)
	}

	fmt.Println(mu)

	var prm RoleMenus
	x.Where("menu_id = ?", mu.Id).And("role_id = ?", roleid).Get(&prm)
	if mu.ThisRoleMenu == true{
		if prm.Id == 0 {
			prm.MenuId = mu.Id
			prm.RoleId, _= strconv.Atoi(roleid)
			x.Insert(&prm)
		}
	}else{
		if prm.Id != 0 {
			x.ID(prm.Id).Delete(&prm)
		}
	}

	return mvc.Response{
        Object : map[string]interface{}{"message": "插入成功", "data":nil, "code":200,},
    }
}

func PutRoleMenuListRec(submenuList []*Menu, roleid string, tx *xorm.Session){

	if submenuList != nil {
		for _, v := range submenuList{

			var rm RoleMenus
			tx.Where("menu_id = ?", v.Id).And("role_id = ?", roleid).Get(&rm)
			if v.ThisRoleMenu == true{
				if rm.Id == 0 {
					rm.MenuId = v.Id
					rm.RoleId, _= strconv.Atoi(roleid)
					tx.Insert(&rm)
				}
			}else{
				if rm.Id != 0 {
					tx.ID(rm.Id).Delete(&rm)
				}
			}

			PutRoleMenuListRec(v.SubmenuList, roleid, tx)
		}
	}
}

func PutRoleMenuListAsync(submenuList []*Menu, roleid string, tx *xorm.Session, c *bool, wg *sync.WaitGroup)  {

	defer func() {
		// 捕获异常 防止waitGroup阻塞
		if err := recover(); err != nil {
			wg.Done()
			*c = true
		}
	}()

	PutRoleMenuListRec(submenuList, roleid, tx)

	wg.Done()

}

func (c *MenuController) PostInsertrolemenuasync() mvc.Result {

	roleid := c.Ctx.FormValue("roleid")
	rolemenustr := c.Ctx.FormValue("rolemenustr")

	fmt.Println(roleid)

	var wg sync.WaitGroup

	var mu Menu
	if err := json.Unmarshal([]byte(rolemenustr), &mu); err == nil{
		var tx_list []*xorm.Session

		wg.Add(4)

		c := false

		for i := 0; i < 4; i++ {
			j := i

			tx := x.NewSession()
			tx_list = append(tx_list, tx)

			go PutRoleMenuListAsync(mu.SubmenuList[j*2 : (j+1)*2], roleid, tx, &c, &wg)

		}

		if c{
			for _, t := range tx_list {
				t.Rollback()
			}
		}else {
			for _, t := range tx_list {
				t.Commit()
			}
		}
	}

	fmt.Println(mu)

	var prm RoleMenus
	x.Where("menu_id = ?", mu.Id).And("role_id = ?", roleid).Get(&prm)
	if mu.ThisRoleMenu == true{
		if prm.Id == 0 {
			prm.MenuId = mu.Id
			prm.RoleId, _= strconv.Atoi(roleid)
			x.Insert(&prm)
		}
	}else{
		if prm.Id != 0 {
			x.ID(prm.Id).Delete(&prm)
		}
	}

	wg.Wait()

	return mvc.Response{
		Object : map[string]interface{}{"message": "插入成功", "data":nil, "code":200,},
	}
}

func (c *MenuController) PostQuerymymenu() mvc.Result { 

	menuid := c.Ctx.FormValue("menuid")
	roleid := c.Ctx.FormValue("roleid")

	fmt.Println(menuid)
	fmt.Println(roleid)

	var pm Menu
	x.Where("id = ?", menuid).Get(&pm)

	var rm RoleMenus
	_, err := x.Where("menu_id = ?", menuid).And("role_id = ?", roleid).Get(&rm)
	if err != nil{
		fmt.Println(err)
	}
	fmt.Println(rm)
	if rm.Id != 0{
		pm.ThisRoleMenu = true
	}
	
	fmt.Println(&pm)
	if pm.Id == 0{
		return mvc.Response{
			Object : map[string]interface{}{"message": "找不到菜单", "data":nil, "code":400,},
		}
	}
	
	GetMyMenuList(&pm, roleid)

	return mvc.Response{
        Object : map[string]interface{}{"message": "成功", "data":pm, "code":200,},
    }
}

func GetMyMenuListAsync(mu *Menu, roleid string){

	x.Join("INNER", "role_menus", "role_menus.menu_id = menu.id").
		Where("menu.parent_menu_id = ?", mu.Id).
		And("role_menus.role_id = ?", roleid).
		Find(&mu.SubmenuList)

	if mu.SubmenuList == nil {
		return
	}else{

		var wg sync.WaitGroup
		wg.Add(4)

		for i := 0; i < 4; i++ {
			k := i

			go func() {
				defer func() {
					// 捕获异常 防止waitGroup阻塞
					if err := recover(); err != nil {
						wg.Done()
					}
				}()

				for _, v := range mu.SubmenuList[k*2:(k+1)*2] {
					var rm RoleMenus
					x.Where("menu_id = ?", v.Id).And("role_id = ?", roleid).Get(&rm)
					if rm.Id != 0 {
						v.ThisRoleMenu = true
					}

					GetMyMenuList(v, roleid)
				}

				wg.Done()
			}()
		}

		wg.Wait()
	}
}

func (c *MenuController) PostQuerymymenuasync() mvc.Result {

	menuid := c.Ctx.FormValue("menuid")
	roleid := c.Ctx.FormValue("roleid")

	fmt.Println(menuid)
	fmt.Println(roleid)

	var pm Menu
	x.Where("id = ?", menuid).Get(&pm)

	var rm RoleMenus
	_, err := x.Where("menu_id = ?", menuid).And("role_id = ?", roleid).Get(&rm)
	if err != nil{
		fmt.Println(err)
	}
	fmt.Println(rm)
	if rm.Id != 0{
		pm.ThisRoleMenu = true
	}

	fmt.Println(&pm)
	if pm.Id == 0{
		return mvc.Response{
			Object : map[string]interface{}{"message": "找不到菜单", "data":nil, "code":400,},
		}
	}

	GetMyMenuListAsync(&pm, roleid)

	return mvc.Response{
		Object : map[string]interface{}{"message": "成功", "data":pm, "code":200,},
	}
}

func main(){ 

	defer x.Close()

	app := iris.New()

	mvc.New(app).Handle(new(MenuController))
	app.Run(iris.Addr(":1324"))
	
}